module service.article_service;

import std.typecons : Nullable;

import d2sqlite3;
import model.article;
import utils.const_val;

/** 
 * 获取所有文章列表
 * Returns: Article[]
 */
Article[] getArticleList ()
{
    Database db = Database(CONN_STR);
    ResultRange results = db.execute("SELECT * FROM article;");

    Article[] articleList = [];
    if (results.empty)
    {
        return articleList;
    }
    foreach (Row row; results)
    {
        auto id = row["id"].as!int;
        auto title = row["title"].as!string;
        auto thumbnail = row["thumbnail"].as!string;
        auto summary = row["summary"].as!string;
        auto isHot = row["isHot"].as!string;
        auto isTop = row["isTop"].as!string;
        auto visible = row["visible"].as!string;
        auto categoryId = row["categoryId"].as!int;
        auto categoryTitle = row["categoryTitle"].as!string;
        auto viewsCount = row["viewsCount"].as!int;
        auto commentsCount = row["commentsCount"].as!int;
        auto createTime = row["createTime"].as!string;
        auto updateTime = row["updateTime"].as!string;

        Article article = {
            id: id, title: title, thumbnail: thumbnail, summary: summary, isHot: isHot, isTop: isTop,
            visible: visible, categoryId: categoryId, categoryTitle: categoryTitle, viewsCount: viewsCount,
            commentsCount: commentsCount, createTime: createTime, updateTime: updateTime
        };
        articleList ~= article;
    }

    return articleList;
}

/** 
 * 分页获取文章列表
 * Params:
 *   pageNum = 页码
 *   pageSize = 每页大小
 * Returns: Article[]
 */
Article[] getArticleListByPaginate (int pageNum, int pageSize)
{
    Database db = Database(CONN_STR);
    Statement statement = db.prepare("SELECT * FROM article LIMIT :offset, :limit;");
    statement.bind(":offset", (pageNum - 1) * pageSize);
    statement.bind(":limit", pageSize);
    ResultRange results = statement.execute();

    Article[] articleList = [];
    if (results.empty)
    {
        return articleList;
    }
    foreach (Row row; results)
    {
        auto id = row["id"].as!int;
        auto title = row["title"].as!string;
        auto thumbnail = row["thumbnail"].as!string;
        auto summary = row["summary"].as!string;
        auto isHot = row["isHot"].as!string;
        auto isTop = row["isTop"].as!string;
        auto visible = row["visible"].as!string;
        auto content = row["content"].as!string;
        auto categoryId = row["categoryId"].as!int;
        auto categoryTitle = row["categoryTitle"].as!string;
        auto viewsCount = row["viewsCount"].as!int;
        auto commentsCount = row["commentsCount"].as!int;
        auto createTime = row["createTime"].as!string;
        auto updateTime = row["updateTime"].as!string;

        Article article = {
            id: id, title: title, thumbnail: thumbnail, summary: summary, isHot: isHot, isTop: isTop,
            visible: visible, content: content, categoryId: categoryId, categoryTitle: categoryTitle, viewsCount: viewsCount,
            commentsCount: commentsCount, createTime: createTime, updateTime: updateTime
        };
        articleList ~= article;
    }

    return articleList;
}

/** 
 * 获取文章总数
 * Returns: int
 */
int getArticleTotalCount ()
{
    Database db = Database(CONN_STR);
    int count = db.execute("SELECT count(id) FROM article;").oneValue!int;

    return count;
}

/** 
 * 获取文章详情
 * Params:
 *   articleId = 文章id
 * Returns: Nullable!Article
 */
Nullable!Article getArticleById (int articleId)
{
    Database db = Database(CONN_STR);
    Statement statement = db.prepare("SELECT * FROM article WHERE id=:id LIMIT 1;");
    statement.bind(":id", articleId);
    ResultRange results = statement.execute();

    Nullable!Article article;
    if (results.empty)
    {
        return article;
    }
    auto row = results.front;
    auto id = row["id"].as!int;
    auto title = row["title"].as!string;
    auto summary = row["summary"].as!string;
    auto content = row["content"].as!string;
    auto categoryId = row["categoryId"].as!int;
    auto categoryTitle = row["categoryTitle"].as!string;
    auto createTime = row["createTime"].as!string;
    auto updateTime = row["updateTime"].as!string;

    article = Article(
id : id,
title:
        title,
summary:
        summary,
content:
        content,
categoryId:
        categoryId,
categoryTitle:
        categoryTitle,
createTime:
        createTime,
updateTime:
        updateTime);

    return article;
}

/** 
 * 获取文章归档列表
 * Returns: ArticleArchive[]
 */
ArticleArchive[] getArticleArchiveList ()
{
    import std.conv : to;

    Article[] articleList = getArticleList();
    ArticleArchive[] archiveList = [];

    foreach (Article article; articleList)
    {
        auto year = to!int(article.createTime[0 .. 4]);

        auto id = article.id;
        auto title = article.title;
        auto summary = article.summary;
        auto categoryTitle = article.categoryTitle;
        auto createTime = article.createTime;

        bool isExist = false;
        foreach (ref ArticleArchive archive; archiveList)
        {
            if (archive.year == year)
            {
                archive.articles ~= Article(id : id,
            title:
                    title,
            summary:
                    summary,
            categoryTitle:
                    categoryTitle,
            createTime:
                    createTime);

                isExist = true;
                break;
            }
        }
        if (!isExist)
        {
            archiveList ~= ArticleArchive(year, [
                Article(id : id,
        title:
                    title,
        summary:
                    summary,
        categoryTitle:
                    categoryTitle,
        createTime:
                    createTime)
            ]);
        }
    }

    return archiveList;
}

/** 
 * 通过分类名称获取文章列表
 * Params:
 *   categoryName = 分类名称
 * Returns: Article[]
 */
Article[] getArticlesByCategoryName (string categoryName)
{
    Database db = Database(CONN_STR);
    Statement statement = db.prepare("SELECT * FROM article WHERE categoryTitle=:categoryTitle;");
    statement.bind(":categoryTitle", categoryName);
    ResultRange results = statement.execute();

    Article[] articleList = [];
    if (results.empty)
    {
        return articleList;
    }
    foreach (Row row; results)
    {
        auto id = row["id"].as!int;
        auto title = row["title"].as!string;
        auto thumbnail = row["thumbnail"].as!string;
        auto summary = row["summary"].as!string;
        auto isHot = row["isHot"].as!string;
        auto isTop = row["isTop"].as!string;
        auto visible = row["visible"].as!string;
        auto categoryId = row["categoryId"].as!int;
        auto categoryTitle = row["categoryTitle"].as!string;
        auto viewsCount = row["viewsCount"].as!int;
        auto commentsCount = row["commentsCount"].as!int;
        auto createTime = row["createTime"].as!string;
        auto updateTime = row["updateTime"].as!string;

        Article article = {
            id: id, title: title, thumbnail: thumbnail, summary: summary, isHot: isHot, isTop: isTop,
            visible: visible, categoryId: categoryId, categoryTitle: categoryTitle, viewsCount: viewsCount,
            commentsCount: commentsCount, createTime: createTime, updateTime: updateTime
        };
        articleList ~= article;
    }

    return articleList;
}

/** 
 * 通过关键字搜索文章
 * Params:
 *   keyword = 关键字
 * Returns: Article[]
 */
Article[] searchArticlesByKeyword (string keyword)
{
    Database db = Database(CONN_STR);
    Statement statement = db.prepare("SELECT * FROM article WHERE title LIKE :keyword;");
    statement.bind(":keyword", "%" ~ keyword ~ "%");
    ResultRange results = statement.execute();

    Article[] articleList = [];
    if (results.empty)
    {
        return articleList;
    }
    foreach (Row row; results)
    {
        auto id = row["id"].as!int;
        auto title = row["title"].as!string;
        auto thumbnail = row["thumbnail"].as!string;
        auto summary = row["summary"].as!string;
        auto isHot = row["isHot"].as!string;
        auto isTop = row["isTop"].as!string;
        auto visible = row["visible"].as!string;
        auto categoryId = row["categoryId"].as!int;
        auto categoryTitle = row["categoryTitle"].as!string;
        auto viewsCount = row["viewsCount"].as!int;
        auto commentsCount = row["commentsCount"].as!int;
        auto createTime = row["createTime"].as!string;
        auto updateTime = row["updateTime"].as!string;

        Article article = {
            id: id, title: title, thumbnail: thumbnail, summary: summary, isHot: isHot, isTop: isTop,
            visible: visible, categoryId: categoryId, categoryTitle: categoryTitle, viewsCount: viewsCount,
            commentsCount: commentsCount, createTime: createTime, updateTime: updateTime
        };
        articleList ~= article;
    }

    return articleList;
}

/** 
 * 添加文章
 * Params:
 *   article = 待添加的文章
 * Returns: bool
 */
bool addArticle (ArticleDtoForCreate article)
{
    Database db = Database(CONN_STR);
    Statement statement = db.prepare("INSERT INTO article (title, thumbnail, summary, isHot, isTop, visible, content, categoryId, categoryTitle, viewsCount, commentsCount, createTime, updateTime) VALUES (:title, :thumbnail, :summary, :isHot, :isTop, :visible, :content, :categoryId, :categoryTitle, :viewsCount, :commentsCount, :createTime, :updateTime);");
    statement.bind(":title", article.title);
    statement.bind(":thumbnail", article.thumbnail);
    statement.bind(":summary", article.summary);
    statement.bind(":isHot", article.isHot);
    statement.bind(":isTop", article.isTop);
    statement.bind(":visible", article.visible);
    statement.bind(":content", article.content);
    statement.bind(":categoryId", article.categoryId);
    statement.bind(":categoryTitle", article.categoryTitle);
    statement.bind(":viewsCount", article.viewsCount);
    statement.bind(":commentsCount", article.commentsCount);
    statement.bind(":createTime", article.createTime);
    statement.bind(":updateTime", article.updateTime);
    try
    {
        statement.execute();
        return db.changes == 1;
    }
    catch (Exception e)
    {
        return false;
    }
}

/** 
 * 更新文章
 * Params:
 *   article = 待更新的文章数据
 * Returns: bool
 */
bool updateArticle (ArticleDtoForUpdate article)
{
    Database db = Database(CONN_STR);
    Statement statement = db.prepare("UPDATE article SET title=:title, thumbnail=:thumbnail, summary=:summary, isHot=:isHot, isTop=:isTop, visible=:visible, content=:content, categoryId=:categoryId, categoryTitle=:categoryTitle, updateTime=:updateTime WHERE id=:id;");
    statement.bind(":title", article.title);
    statement.bind(":thumbnail", article.thumbnail);
    statement.bind(":summary", article.summary);
    statement.bind(":isHot", article.isHot);
    statement.bind(":isTop", article.isTop);
    statement.bind(":visible", article.visible);
    statement.bind(":content", article.content);
    statement.bind(":categoryId", article.categoryId);
    statement.bind(":categoryTitle", article.categoryTitle);
    statement.bind(":updateTime", article.updateTime);
    statement.bind(":id", article.id);

    try
    {
        ResultRange results = statement.execute();
        return results.empty;
    }
    catch (Exception e)
    {
        return false;
    }
}

/** 
 * 删除文章
 * Params:
 *   id = 文章id
 * Returns: bool
 */
bool deleteArticle (int id)
{
    Database db = Database(CONN_STR);
    Statement statement = db.prepare("DELETE FROM article WHERE id=:id;");
    statement.bind(":id", id);
    ResultRange results = statement.execute();

    // return db.changes == 1;
    return results.empty;
}

/** 
 * 批量删除文章
 * Params:
 *   ids = 待上传的文章id数组
 * Returns: bool
 */
bool batchDeleteArticles (int[] ids)
{
    import std.array : join;
    import std.algorithm.iteration : map;
    import std.conv : to;

    alias stringize = map!(to!string); // 将int数组转换为string数组

    Database db = Database(CONN_STR);
    string sqlStr1 = `DELETE FROM article WHERE id IN (`;
    string sqlStr2 = `);`;
    string idsStr = stringize(ids).join(",");
    string sqlAll = sqlStr1 ~ idsStr ~ sqlStr2;
    try
    {
        db.begin();
        ResultRange results = db.execute(sqlAll);
        if (db.totalChanges != ids.length)
        {
            db.rollback();
            return false;
        }
        db.commit();

        return results.empty;
    }
    catch (Exception e)
    {
        return false;
    }
}
